package com.bc.trade.channel.suning;

import java.security.PrivateKey;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.net.ssl.SSLContext;

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bc.common.CommonEnum.OrderStatus;
import com.bc.common.CommonEnum.Resp;
import com.bc.common.message.DFReq;
import com.bc.common.message.DFResp;
import com.bc.trade.channel.IChannel;
import com.bc.trade.channel.suning.util.Digest;
import com.bc.trade.channel.suning.util.RSAUtil;
import com.bc.trade.message.QueryOrderResp;
import com.bc.trade.message.QuickPayReq;
import com.bc.trade.message.QuickPayResp;
import com.bc.trade.message.QuickPaySetReq;
import com.bc.trade.message.QuickPaySetResp;
import com.bc.trade.message.UnionPayReq;
import com.bc.trade.message.UnionPayResp;
import com.bc.trade.model.TradeOrder;

public class SuningChannel implements IChannel {

    private String merchantNo = "70243022";

	private String notifyUrl = "http://pay.lihaishop.com:8080/notify/suningCallback"; 

	@Override
	public QueryOrderResp query(TradeOrder order) throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public QueryOrderResp dfQuery(TradeOrder order) throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public DFResp df(DFReq req, TradeOrder order) throws Exception {
		DFResp rsp = new DFResp(req);
		// 苏宁易付宝账户间转账特殊标记
		if ("#suning".equals(req.getBankType())) {
			// 渠道账户间转账
			transferAcquire(order, req, rsp);
		} else {
			// 代付到银行卡
			withdraw(order, req, rsp);
		}
		return rsp;
	}

	/** 代付到银行卡 */
	private void withdraw(final TradeOrder order, final DFReq req, final DFResp rsp) throws Exception {
		// 明细
	    JSONObject detailObject = new JSONObject();
        detailObject.put("serialNo", order.getOrderNo());
        detailObject.put("receiverType", "PERSON");
        detailObject.put("receiverCurrency", "CNY");
        detailObject.put("receiverName", req.getAccountName());
        detailObject.put("amount", order.getAmount());	// 金额，单位“分”
        detailObject.put("orderName", "还款");
        detailObject.put("bankName", req.getBankName());
        detailObject.put("receiverCardNo", req.getAccountCardNo());
        detailObject.put("bankCode", req.getBankType());
        detailObject.put("bankProvince", req.getProvince());
        detailObject.put("bankCity", req.getCity());
        detailObject.put("payeeBankLinesNo", "");
	    JSONArray detailArray = new JSONArray();
        detailArray.add(detailObject);
        // 批内容
        JSONObject contentObject = new JSONObject();
        contentObject.put("batchNo", order.getOrderNo());	// 批次都是单笔，设置为与订单号一样
        contentObject.put("merchantNo", merchantNo);
        contentObject.put("productCode", "01070000042");	// 固定：批付到卡产品编码
        contentObject.put("totalNum", 1);		// 批次里只有一笔
        contentObject.put("totalAmount", order.getAmount());	// 总金额就是单笔的金额，单位“分”
        contentObject.put("currency", "CNY");
        contentObject.put("payDate", LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")));
        contentObject.put("tunnelData", "{}");
        contentObject.put("goodsType", "220072");	// 固定：商品编码
        contentObject.put("remark", "个人出款");
        contentObject.put("detailData", detailArray);
        contentObject.put("notifyUrl", notifyUrl);
        contentObject.put("batchOrderName", "批量出款");
	    
	    // body批次固定每次只单付1笔
        JSONArray jsonArray = new JSONArray();
        jsonArray.add(contentObject);
        String bussinessParam = jsonArray.toJSONString();
        
        // 发送请求
	    String url = "https://wag.yifubao.com/epps-wag/withdraw.htm";
        String rspJson = send(url, bussinessParam);
		JSONObject resultMap = JSONObject.parseObject(rspJson);
		
		String responseCode = null;
		String responseMsg = null;
		String subkey = order.getOrderNo() + "_" + merchantNo;
		// 如果外层成功，则需要到内部取特定订单的返回码
		if(resultMap.containsKey("responseCode")) {
			responseCode = resultMap.getString("responseCode");
			responseMsg = resultMap.getString("responseMsg");
		} else if(resultMap.containsKey(subkey)) {
			JSONObject detailMap = resultMap.getJSONObject(subkey);
			responseCode = detailMap.getString("responseCode");
			responseMsg = detailMap.getString("responseMsg");
		}
		if ("0000".equals(responseCode)) {
			order.setOrderStatus(OrderStatus.InProcess.getValue());
			order.setRespCode(Resp.InProcess.getCode());
			order.setRespMsg(responseMsg);
			rsp.setRespCode(Resp.Success.getCode());
			rsp.setRespMsg(Resp.Success.getMsg());
		} else {
			order.setOrderStatus(OrderStatus.Fail.getValue());
			order.setRespCode(Resp.Fail.getCode());
			order.setRespMsg(responseMsg);
			rsp.setRespCode(Resp.Fail.getCode());
			rsp.setRespMsg("CH" + order.getRespMsg());
		}
	}
	
	/** 账户间转账 */
	private void transferAcquire(final TradeOrder order, final DFReq req, final DFResp rsp) throws Exception {
		// 构造批次json(实际上按单笔使用)
		JSONArray detailArray = new JSONArray();
		JSONObject detailObject = new JSONObject();
        detailObject.put("serialNo", order.getOrderNo());
        detailObject.put("receiverNo", req.getAccountCardNo());	            // 收款方【商户号】          （仅收款方为商户时传递）
        detailObject.put("receiverLoginName", req.getAccountCardNo()); // 收款方【易付宝登录名】（仅收款方为个人时传递）
        detailObject.put("receiverType", "PERSON");					    // 收款方类型(PERSON：个人；CORP：企业)
        detailObject.put("receiverName", req.getAccountName()); // 收款方姓名(收款方为个人时传递，收款方为商户时可空)
        detailObject.put("amount", order.getAmount());
        detailObject.put("orderName", "转账");
        detailObject.put("remark", "转账到易付宝账户");
        detailArray.add(detailObject);
        
		// 业务json
        JSONObject contentObject = new JSONObject();
        contentObject.put("batchNo", order.getOrderNo());
        contentObject.put("merchantNo", merchantNo);
        contentObject.put("productCode", "01060000029");
        contentObject.put("totalNum", 1);
        contentObject.put("totalAmount", order.getAmount());
        contentObject.put("currency", "CNY");
        contentObject.put("chargeMode", "01");
        contentObject.put("payDate", LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")));
        contentObject.put("tunnelData", "{}");
        contentObject.put("goodsType", "220072");
        contentObject.put("remark", "转账");
        contentObject.put("notifyUrl", "http://pay.lihaishop.com:8080/notify/suningCallback");
        contentObject.put("batchOrderName", "批量转账");
        contentObject.put("detailData", detailArray);
        String bussinessParam = contentObject.toJSONString();

        // 发送请求
	    String url = "https://tag.yifubao.com/epps-twg/transferAcquire.htm";
        String rspJson = send(url, bussinessParam);
		JSONObject resultMap = JSONObject.parseObject(rspJson);
		
		String responseCode = null;
		String responseMsg = null;
		String subkey = order.getOrderNo() + "_" + merchantNo;
		// 如果外层成功，则需要到内部取特定订单的返回码
		if(resultMap.containsKey("responseCode")) {
			responseCode = resultMap.getString("responseCode");
			responseMsg = resultMap.getString("responseMsg");
		} else if(resultMap.containsKey(subkey)) {
			JSONObject detailMap = resultMap.getJSONObject(subkey);
			responseCode = detailMap.getString("responseCode");
			responseMsg = detailMap.getString("responseMsg");
		}
		if ("0000".equals(responseCode)) {
			order.setOrderStatus(OrderStatus.InProcess.getValue());
			order.setRespCode(Resp.InProcess.getCode());
			order.setRespMsg(responseMsg);
			rsp.setRespCode(Resp.Success.getCode());
			rsp.setRespMsg(Resp.Success.getMsg());
		} else {
			order.setOrderStatus(OrderStatus.Fail.getValue());
			order.setRespCode(Resp.Fail.getCode());
			order.setRespMsg(responseMsg);
			rsp.setRespCode(Resp.Fail.getCode());
			rsp.setRespMsg("CH" + order.getRespMsg());
		}
	}

	private String send(String url, String bussinessParam) throws Exception {
		// 签名
        String singnature = calculateSign(bussinessParam);
        // 请求参数
        List<NameValuePair> params = new ArrayList<>();
        params.add(new BasicNameValuePair("publicKeyIndex", "0001"));		// 固定：公钥索引
        params.add(new BasicNameValuePair("signAlgorithm", "RSA"));		// 固定：签名算法
        params.add(new BasicNameValuePair("merchantNo", merchantNo));
        params.add(new BasicNameValuePair("signature", singnature));
        params.add(new BasicNameValuePair("inputCharset", "UTF-8"));
        params.add(new BasicNameValuePair("body", bussinessParam));
        
        // 请求
		System.out.println("请求suning发送报文：-->[" + params.toString() + "]");
        String rspJson = httpsPost(url, params);
		System.out.println("接收suning响应报文：<--[" + rspJson + "]");
		return rspJson;
	}

	private String calculateSign(String bussinessParam) throws Exception {
		Map<String, String> signMap = new HashMap<String, String>();
        signMap.put("merchantNo", merchantNo);
        signMap.put("publicKeyIndex", "0001");		// 固定
        signMap.put("inputCharset", "UTF-8");
        signMap.put("body", bussinessParam);
        // 私钥签名
        String selfPrivateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANQ6/b8MVIH/LsmzM/IlJRxNeuGHhVME1MNcEZ4Y+dBuvdj5aAE/HS5+WlCANaWWQxZEBIksMIB/ATWAX/pBaYKh10S35ZcGa7vx6FDFfwNSHMjfsbyI+KBoLT3ks2sDQmQMMQ9Wy/xOGZwBzprqA9fZ82qYF5g0CDiwR8mqoh4VAgMBAAECgYAupERN72THKRDcTSD2b4lnXfPKiuEXLLGhuaM5jp2gXCYnADEGTRuZszUgCf1ARfS6ePpSNz71jnPykSMGucd5AqQayNWTgYuqzSkQEZz5un1DQMVsxkIPY/lP/aX3QyX5sw+fSKP5yo3b/RiA69gffv6haY+XT9p9V55PwSy/oQJBAO46QueQQAg4CSKVnxS6Ggqoi6hUBnNLJck6iCVfT1hKRLbeLpjq2554OwDq2Ct/Rbr9/PUzdhtmnKqAf7PXYI0CQQDkEDqV1RyRQvXSOP0clf/YC9tH0BPm4aby5ntlUkdoZq4KawbYO9bBFzqqZG1MTQVQB9pNt7xeBll07QjQOyWpAkA3a1pRIF+lCdQmNYRyf/C69ga3PATcIWlC7NjrSMbelThzCjDw5gH9VZ70Ua3EQLFqFNgZ+sTJDw/3IjmTDtGpAkEAgxTg12WZ+dPmvmGK+XctidumuWvbRITGFC5tFhtod5Usy0ggswARuwHC8lSujYBnYUxiKPFGDzTPDel/e0/DoQJBALJyaLeJgZJk39ACApbIkw2bA0UeyaGIuPYa7r2tKwlC8/51pFa1l/IrbPgLyDFdH4sjOckUhM8cpVgwdddyAmY=";
        String digest = Digest.digest(Digest.mapToString(Digest.treeMap(signMap)));
        PrivateKey privateKey = RSAUtil.getPrivateKey(selfPrivateKey);
        String singnature = RSAUtil.sign(digest, privateKey);
		return singnature;
	}

	// 请求服务器发送HTTPS POST
	private String httpsPost(String url, List<NameValuePair> params) throws Exception {
        HttpPost post = new HttpPost(url);
        post.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
        RequestConfig config = RequestConfig.custom().setConnectTimeout(3000).setSocketTimeout(9000).build();
        post.setConfig(config);
        SSLContext sslctx = SSLContexts.custom()
        		.useProtocol("SSL")
        		.loadTrustMaterial(null,(chain, authType) -> {return true;})
        		.build();
        CloseableHttpClient httpclient = HttpClients.custom()
        		.setSSLContext(sslctx)
        		.setSSLHostnameVerifier(new NoopHostnameVerifier())
        		.build();
        CloseableHttpResponse response = httpclient.execute(post);
        try {
        	HttpEntity httpEntity = response.getEntity();
        	if(httpEntity != null) {
            	return EntityUtils.toString(httpEntity);	
        	}
        } finally {
            response.close();
        }
		return null;
	}

	@Override
	public UnionPayResp unionpay(UnionPayReq req, TradeOrder order) throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public QuickPaySetResp quickPaySet(QuickPaySetReq req, TradeOrder order) throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public QuickPayResp quickPay(QuickPayReq req, TradeOrder order) throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

}
